using System;

using CodingBlog.Core.ExtensionMethods;
using CodingBlog.Core.Model;
using CodingBlog.Core.Repository.Contract;
using CodingBlog.Core.Service;

using Moq;

using NUnit.Framework;

namespace CodingBlog.Core.Tests.Service
{
    public class UserServiceTests
    {
        [Test]
        public void can_create_user_with_hashed_password()
        {
            var testUserName = "test user name";
            var testEmail = "testuser@codingblog.com";
            var testPassword = "secret_password";

            var fakeUser = GetFakeUser(testUserName, testEmail, testPassword);

            var userRepositoryMock = new Mock<IUserRepository>();
            userRepositoryMock.Setup(repo => repo.CreateUser(fakeUser)).Returns(fakeUser);

            var userCreated = new UserService(userRepositoryMock.Object).CreateUser(fakeUser);

            userRepositoryMock.Verify(repo => repo.CreateUser(fakeUser), Times.Once());

            string computedHash = testPassword.ComputeHash();

            Assert.NotNull(userCreated);
            Assert.AreEqual(testUserName, userCreated.Name);
            Assert.AreEqual(testEmail, userCreated.Email);
            Assert.AreEqual(computedHash, userCreated.Password);
        }

        [Test]
        public void can_get_user_by_id()
        {
            var testUserName = "test user name";
            var testEmail = "testuser@codingblog.com";
            var testPassword = "secret_password";

            var fakeUser = GetFakeUser(testUserName, testEmail, testPassword);

            var userRepositoryMock = new Mock<IUserRepository>();
            userRepositoryMock.Setup(repo => repo.GetUserById(1)).Returns(fakeUser);

            var userFetched = new UserService(userRepositoryMock.Object).GetUserById(1);

            userRepositoryMock.Verify(repo => repo.GetUserById(1), Times.Once());

            Assert.NotNull(userFetched);
            Assert.AreEqual(testUserName, userFetched.Name);
            Assert.AreEqual(testEmail, userFetched.Email);
        }

        [TestCase(true, Result = true)]
        [TestCase(false, Result = false)]
        public bool can_check_if_email_is_in_use(bool repositoryResult)
        {
            var emailInUse = "testuser@codingblog.com";

            var userRepositoryMock = new Mock<IUserRepository>();
            userRepositoryMock.Setup(repo => repo.EmailIsInUse(emailInUse)).Returns(repositoryResult);

            var result = new UserService(userRepositoryMock.Object).EmailIsInUse(emailInUse);

            userRepositoryMock.Verify(repo => repo.EmailIsInUse(emailInUse), Times.Once());

            return result;
        }

        [Test]
        public void when_credentials_are_correct_return_user_instance_from_validate_user()
        {
            var testUserName = "test user name";
            var testEmail = "testuser@codingblog.com";
            var testPassword = "secret_password";
            var testPasswordHashed = testPassword.ComputeHash();

            var fakeUser = GetFakeUser(testUserName, testEmail, testPasswordHashed);

            var userRepositoryMock = new Mock<IUserRepository>();
            userRepositoryMock.Setup(repo => repo.GetUserByEmailAndPassword(testEmail, testPasswordHashed)).Returns(fakeUser);

            var validatedUser = new UserService(userRepositoryMock.Object).ValidateUser(testEmail, testPassword);

            userRepositoryMock.Verify(repo => repo.GetUserByEmailAndPassword(testEmail, testPasswordHashed), Times.Once());

            Assert.NotNull(validatedUser);
            Assert.AreEqual(testUserName, validatedUser.Name);
            Assert.AreEqual(testEmail, validatedUser.Email);
        }

        [Test]
        public void when_credentials_are_incorrect_return_null_from_validate_user()
        {
            var testEmail = "testuser@codingblog.com";
            var testPassword = "secret_password";
            var testPasswordHashed = testPassword.ComputeHash();

            var userRepositoryMock = new Mock<IUserRepository>();
            userRepositoryMock.Setup(repo => repo.GetUserByEmailAndPassword(testEmail, testPasswordHashed)).Returns((Func<User>) null);

            var validatedUser = new UserService(userRepositoryMock.Object).ValidateUser(testEmail, testPassword);

            userRepositoryMock.Verify(repo => repo.GetUserByEmailAndPassword(testEmail, testPasswordHashed), Times.Once());

            Assert.Null(validatedUser);
        }

        private static User GetFakeUser(string userName, string email, string password)
        {
            return new User
                       {
                           Name = userName,
                           Email = email,
                           Password = password
                       };
        }
    }
}